using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Content;

namespace PrisonStep
{
    /// <summary>
    /// This class describes our player in the game. 
    /// </summary>
    public class Player
    {
        #region Fields

        private enum States { Start, StanceStart, Stance, WalkStart, WalkLoopStart, WalkLoop, TurnStart, LeftTurn, RightTurn, Crouch, Crouching, StartRaiseBazooka, RaisingBazooka, EndRaiseBazooka, Aiming, LowerBazooka }

        /// <summary>
        /// Game that uses this player
        /// </summary>
        private PrisonGame game;

        //
        // Player location information.  We keep a x/z location (y stays zero)
        // and an orientation (which way we are looking).
        //

        /// <summary>
        /// Player location in the prison. Only x/z are important. y still stay zero
        /// unless we add some flying or jumping behavior later on.
        /// </summary>
        private Vector3 location = new Vector3(0, 0, 600); //new Vector3(275, 0, 1053);

        /// <summary>
        /// The player orientation as a simple angle
        /// </summary>
        private float orientation = 3.14f; // 1.6f;

        /// <summary>
        /// The player transformation matrix. Places the player where they need to be.
        /// </summary>
        private Matrix transform;

        /// <summary>
        /// The rotation rate in radians per second when player is rotating
        /// </summary>
        private float mPanRate = 2;

        /// <summary>
        /// The rate at which victoria will rotate when aiming.
        /// </summary>
        private float mPitchRate = 2;

        /// <summary>
        /// The current rotation that victoria's spine is at.
        /// This is rotated around the X-Axis to be implemented.
        /// </summary>
        private float mAzimuth = 0;

        /// <summary>
        /// The current rotation that victoria's spine is at.
        /// This is rotated around the Z-Axis to be implemented.
        /// </summary>
        private float mElevation = 0;

        // The section we start in
        private string LastRegion = "R_Section1";


        /// <summary>
        /// Our bazooka model
        /// </summary>
        private Bazooka mBazooka = null;
        /// <summary>
        /// Our player model
        /// </summary>
        private AnimatedModel mVictoria = null;

        /// <summary>
        /// Our players current state
        /// </summary>
        private States mState = States.Start;

        /// <summary>
        /// Are we currently moving forward?
        /// </summary>
        private bool mForward = false;

        /// <summary>
        /// The bone index for Victoria's hand
        /// </summary>
        private int mHandBone;

        /// <summary>
        /// Where the bazooka is relative to the world
        /// </summary>
        Matrix mBazookaMatrix;

        /// <summary>
        /// Whether or not out character is aiming the bazooka
        /// </summary>
        private bool mAiming = false;

        private float mCameraDistance = 0.0f;

        private KeyboardState mLastKeyBoardState;



        private BoundingCylinder mBoundingCyl;
        #endregion

        #region Properties

        public Vector3 VictoriaLocation { get { return location; } }

        public Matrix BazMat { get { return mBazookaMatrix; } }

        public string Region { get { return LastRegion; } }

        public Bazooka BazookaProp { get { return mBazooka; } }

        public BoundingCylinder BoundingCyl { get { ComputeBoundingCylinder(); return mBoundingCyl; } }

        public float SlimeWeight { get { return mVictoria.SlimeWeight; } set { mVictoria.SlimeWeight = value; mBazooka.SlimeWeight = value; } }
        #endregion


        public Player(PrisonGame game)
        {
            this.game = game;
            SetPlayerTransform();

            mCameraDistance = new Vector3(-transform.Backward.X * 300, 200, -transform.Backward.Z * 300).Length();

            mVictoria = new AnimatedModel(game, "Victoria");
            mVictoria.AddAssetClip("dance", "Victoria-dance");
            mVictoria.AddAssetClip("stance", "Victoria-stance");
            mVictoria.AddAssetClip("walk", "Victoria-walk");
            mVictoria.AddAssetClip("walkstart", "Victoria-walkstart");
            mVictoria.AddAssetClip("walkloop", "Victoria-walkloop");
            mVictoria.AddAssetClip("rightturn", "Victoria-rightturn");
            mVictoria.AddAssetClip("leftturn", "Victoria-leftturn");
            mVictoria.AddAssetClip("lowerbazooka", "Victoria-lowerbazooka");
            mVictoria.AddAssetClip("raisebazooka", "Victoria-raisebazooka");
            mVictoria.AddAssetClip("crouchbazooka", "Victoria-crouchbazooka");
            mVictoria.AddAssetClip("walkloopbazooka", "Victoria-walkloopbazooka");
            mVictoria.AddAssetClip("walkstartbazooka", "Victoria-walkstartbazooka");
            mVictoria.Skinned = true;

            mVictoria.Region = "R_Section2";
            mVictoria.Adjacent = 2;
            mBoundingCyl = new BoundingCylinder(location, 200, 30);

            mBazooka = new Bazooka(this.game);
            mBazooka.Region = mVictoria.Region;
            mBazooka.Adjacent = mVictoria.Adjacent;
        }

        /// <summary>
        /// Set the value of transform to match the current location
        /// and orientation.
        /// </summary>
        private void SetPlayerTransform()
        {
            transform = Matrix.CreateRotationY(orientation);
            transform.Translation = location;
            if (mVictoria != null)
            {
                Matrix lTorsoTransform = mVictoria.GetBoneTransforms("Bip01 Spine1");
                if (mAzimuth >= -1.0f || mAzimuth <= 1.0f)
                    lTorsoTransform *= Matrix.CreateRotationX(mAzimuth);

                lTorsoTransform *= Matrix.CreateRotationZ(mElevation);

                mVictoria.SetBoneTransforms("Bip01 Spine1", lTorsoTransform);

            }

        }


        public void LoadContent(ContentManager content)
        {
            mVictoria.LoadContent(content);
            mHandBone = mVictoria.model.Bones["Bip01 R Hand"].Index;
            mBazooka.LoadContent(content);
            AnimationPlayer lPlayer = mVictoria.PlayClip("lowerbazooka");
        }

        public void Update(GameTime gameTime)
        {
            double lDeltaTotal = gameTime.ElapsedGameTime.TotalSeconds;

            do
            {
                KeyboardState lKeyboardState = Keyboard.GetState();
                double lDelta = lDeltaTotal;
                bool lLeft = false;
                bool lRight = false;
                KeyboardState keyboardState = Keyboard.GetState();

                if (keyboardState.IsKeyDown(Keys.Left))
                {
                    lLeft = true;
                }

                if (keyboardState.IsKeyDown(Keys.Right))
                {
                    lRight = true;
                }

                if (lKeyboardState.IsKeyDown(Keys.LeftControl) && mLastKeyBoardState.IsKeyUp(Keys.LeftControl))
                {
                    mAiming = !mAiming;
                }

                else 

                if (keyboardState.IsKeyDown(Keys.Up))
                    mForward = true;
                
                else if (keyboardState.IsKeyUp(Keys.Up))
                    mForward = false;

                //
                // State machine will go here
                //
                switch (mState)
                {
                    case States.Start:
                        mState = States.StanceStart;
                        lDelta = 0;
                        break;

                    case States.StanceStart:
                        mVictoria.PlayClip("raisebazooka").Speed = 0;
                        mState = States.Stance;
                        break;

                    case States.Stance:
                        if(mForward)
                        {
                            mState = States.WalkStart;
                            mVictoria.PlayClip("walkstartbazooka");
                        }
                        if (lLeft || lRight)
                        {
                            mState = States.TurnStart;
                        }
                        if (mAiming)
                            mState = States.StartRaiseBazooka;
                        break;
                    case States.TurnStart:
                        location.Y = 0;
                        if (lLeft)
                        {
                            mVictoria.PlayClip("leftturn");
                            mState = States.LeftTurn;
                        }
                        else if (lRight)
                        {
                            mState = States.RightTurn;
                            mVictoria.PlayClip("rightturn");
                        }
                        else
                            mState = States.Stance;
                        break;

                    case States.LeftTurn:
                        if (mVictoria.FinishedAnimation() && lLeft)
                        {
                            mState = States.TurnStart;
                            lDelta = 0;
                        }
                        else if (!lLeft)
                            mState = States.StanceStart;
                        break;

                    case States.RightTurn:
                        if (mVictoria.FinishedAnimation() && lRight)
                        {
                            mState = States.TurnStart;
                            lDelta = 0;
                        }
                        else if (!lRight)
                            mState = States.StanceStart;
                        break;

                    case States.WalkLoop:

                        if (!mForward)
                        {
                            lDelta = 0;
                            mState = States.StanceStart;
                        }
                        else if (mVictoria.FinishedAnimation())
                        {
                            mState = States.WalkLoopStart;
                            mVictoria.PlayClip("walkloopbazooka");
                        }
                        break;

                    case States.WalkLoopStart:
                        location.Y = 0.0f;
                        
                        lDelta = 0;
                        mState = States.WalkLoop;
                        break;

                    case States.WalkStart:
                        if (!mForward)
                        {
                            mState = States.StanceStart;
                            mVictoria.PlayClip("stance");
                        }
                        else if (mVictoria.FinishedAnimation())
                        {

                            mState = States.WalkLoopStart;
                            mVictoria.PlayClip("walkloopbazooka");
                        }
                        break;
                    case States.StartRaiseBazooka:
                        mVictoria.PlayClip("raisebazooka").Speed = 1;
                        mState = States.RaisingBazooka;

                        break;

                    case States.RaisingBazooka:
                        if (!mAiming)
                        {
                            mVictoria.PlayClip("lowerbazooka");
                            mState = States.LowerBazooka;
                        }
                        if (mVictoria.FinishedAnimation())
                        {
                            mState = States.EndRaiseBazooka;
                        }

                        break;
                    case States.EndRaiseBazooka:

                        if (!mAiming)
                        {
                            mAzimuth = 0;
                            mElevation = 0;
                            mVictoria.PlayClip("lowerbazooka").Speed = 1;
                            
                            mState = States.LowerBazooka;
                        }
                        else
                        {
                            mAzimuth += GetPanRate(lKeyboardState) * (float)lDelta;
                            mElevation += GetPitchRate(lKeyboardState) * (float)lDelta;
                        }
                        if (lKeyboardState.IsKeyDown(Keys.Space) && mLastKeyBoardState.IsKeyUp(Keys.Space))
                        {
                            Ray lRay = new Ray(BazMat.Translation, BazMat.Backward);
                            if (game.Alien.BoundingCyl.Intersect(lRay))
                            {
                                //System.Diagnostics.Trace.WriteLine("alien");
                                mBazooka.FireBazooka(2);
                            }
                            else if (game.GortModel.BoundingCyl.Intersect(lRay))
                            {
                                //System.Diagnostics.Trace.WriteLine("gort");
                                mBazooka.FireBazooka(3);
                            }
                            else
                            {
                                mBazooka.FireBazooka(1);
                            }
                            
                        }
                        break;
                    case States.LowerBazooka:
                        if (mVictoria.FinishedAnimation())
                        {
                            mState = States.StanceStart;
                            mVictoria.PlayClip("stance");
                        }
                        break;
                }
                

                // 
                // State update
                //




                mVictoria.Update(lDelta);
                mBazooka.Update(gameTime);

                //
                // Part 1:  Compute a new orientation
                //

                Matrix lDeltaMatrix = mVictoria.DeltaMatrix;
                float lDeltaAngle = (float)Math.Atan2(lDeltaMatrix.Backward.X, lDeltaMatrix.Backward.Z);
                float lNewOrientation = orientation + lDeltaAngle;

                //mAzimuth += lDeltaAngle;

                if (mAzimuth > 1.0)
                    mAzimuth = 1.0f;
                else if (mAzimuth < -1.0)
                    mAzimuth = -1.0f;

                if (mElevation > 0.5f)
                    mElevation = 0.5f;
                else if (mElevation < -0.5f)
                    mElevation = -0.5f;



                //
                // Part 2:  Compute a new location
                //

                // We are likely rotated from the angle the model expects to be in
                // Determine that angle.
                Matrix lRootMatrix = mVictoria.RootMatrix;
                float lActualAngle = (float)Math.Atan2(lRootMatrix.Backward.X, lRootMatrix.Backward.Z);
                Vector3 lNewLocation = location + Vector3.TransformNormal(mVictoria.mDeltaPosition, Matrix.CreateRotationY(lNewOrientation - lActualAngle))*3;

                Vector3 lWallNormal = Vector3.Zero;

                //
                // I'm just taking these here.  You'll likely want to add something 
                // for collision detection instead.
                //
                string region = game.TestRegionNoWall(lNewLocation, ref lWallNormal);

                if (LastRegion == "")
                    LastRegion = region;

                bool lCollision = TestForCollision(region);
               // System.Diagnostics.Trace.WriteLine("region: " + region);
                if (region != "" && lCollision)
                    location = lNewLocation;
                if (region != LastRegion)
                    location = lNewLocation;

                LastRegion = region;
                mVictoria.Region = LastRegion;
                // Since our bazooka is attached to the player it will be in the same room
                // as the player
                BazookaProp.Region = LastRegion;
                orientation = lNewOrientation;
                SetPlayerTransform();
                mVictoria.ComputeAbsoluteTransforms();

               mBazookaMatrix = Matrix.CreateRotationX(MathHelper.ToRadians(109.5f)) *
                        Matrix.CreateRotationY(MathHelper.ToRadians(9.7f)) *
                        Matrix.CreateRotationZ(MathHelper.ToRadians(72.9f)) *
                        Matrix.CreateTranslation(-9.6f, 11.85f, 21.1f) *
                        mVictoria.AbsoTransforms("Bip01 R Hand") *
                        transform;

                mLastKeyBoardState = lKeyboardState;

                lDeltaTotal -= lDelta;
            } while(lDeltaTotal > 0);
            //System.Diagnostics.Trace.WriteLine("location: " + location);

            
                game.Camera.FakeEye = location + new Vector3(0, 180, 0) + -300 * transform.Backward;

               game.Camera.FakeCenter = game.Camera.FakeEye + transform.Backward + new Vector3(0, -0.3f, 0);
            

            
            Matrix lCameraMatrix = Matrix.CreateRotationY(mAzimuth) * transform;
            Vector3 lNewCamLocation = location + new Vector3(-lCameraMatrix.Backward.X * 100, 180 + mElevation * 100, -lCameraMatrix.Backward.Z * 150);

            Vector3 lNormal = Vector3.Zero;

            string lCamRegion = game.TestRegionNoWall(lNewCamLocation, ref lNormal);

            if (lCamRegion == "")
            {
                // The camera is no longer in the bounds of the collision map
                Vector3 lEyeToPlayer = location - game.Camera.DesiredEye;

                if (lEyeToPlayer.Length() <= mCameraDistance)
                {
                    game.Camera.DesiredEye = game.SlideOnWall(game.Camera.DesiredEye, lNewCamLocation, lNormal);
                }
                else
                {
                    game.Camera.DesiredEye = lNewCamLocation;
                }

            }
            else
                game.Camera.DesiredEye = lNewCamLocation;

            // Now to keep our camera on our player
            game.Camera.Center = new Vector3(location.X, location.Y + 140, location.Z);
            game.Camera.DesiredUp = transform.Up;
        }

        private float GetPanRate(KeyboardState aKeyboardState)
        {
            if (aKeyboardState.IsKeyDown(Keys.A))
                return mPanRate;
            else if (aKeyboardState.IsKeyDown(Keys.D))
                return -mPanRate;
            else
                return 0;
        }

        private float GetPitchRate(KeyboardState aKeyboardState)
        {
            if (aKeyboardState.IsKeyDown(Keys.W))
                return mPitchRate;
            else if (aKeyboardState.IsKeyDown(Keys.S))
                return -mPitchRate;
            else
                return 0;

        }


        /// <summary>
        /// This function is called to draw the player.
        /// </summary>
        /// <param name="graphics"></param>
        /// <param name="gameTime"></param>
        public void Draw(GraphicsDeviceManager graphics, GameTime gameTime)
        {
            Matrix lTransform = Matrix.CreateRotationY(orientation);
            lTransform.Translation = location;
            mVictoria.Draw(graphics, gameTime, lTransform);

            // This should rotate and place the bazooka in the hands of victoria
            mBazooka.Draw(graphics, gameTime, mBazookaMatrix);

            
        }

        private bool TestForCollision(string aRegion)
        {
            bool lTemp = false;

            if (aRegion.StartsWith("R_Door"))
            {
                int lSection = (int)Char.GetNumericValue(aRegion[aRegion.Length - 1]);
                Vector3 lVect1 = location + new Vector3(0, 180, 0);
                Vector3 lVect2 = lVect1 + transform.Backward + new Vector3(0, -0.1f, 0);
                Vector3 lVect3 = lVect2 - lVect1;

                foreach (PrisonModel lModel in game.Models)
                {
                    bool lTemp2 = lModel.CheckForOpenDoors(lSection, lVect3, lVect1);

                    lTemp = lTemp || lTemp2;
                }
            }
            else
                lTemp = true;

            if (aRegion.StartsWith("R_Section"))
            {

                for (int i = 0; i < game.Models.Count; i++)
                {
                    if (aRegion.EndsWith("1"))
                    {
                        game.Slimed = false;
                        //So we don't reload untill we leave and come back to region 1
                        if (LastRegion != aRegion)
                            mBazooka.ReloadBazooka();
                    }

                    if (game.Slimed)
                        game.Models[i].SlimeWeight = game.SlimeWeight;
                    game.Models[i].CloseOpenDoors();
                }

            }
            return lTemp;
        }


        private void ComputeBoundingCylinder()
        {
            mBoundingCyl.Location = location;
        }

    }
}
